// filehdr.cc 
//	Routines for managing the disk file header (in UNIX, this
//	would be called the i-node).
//
//	The file header is used to locate where on disk the 
//	file's data is stored.  We implement this as a fixed size
//	table of pointers -- each entry in the table points to the 
//	disk sector containing that portion of the file data
//	(in other words, there are no indirect or doubly indirect 
//	blocks). The table size is chosen so that the file header
//	will be just big enough to fit in one disk sector, 
//
//      Unlike in a real system, we do not keep track of file permissions, 
//	ownership, last modification date, etc., in the file header. 
//
//	A file header can be initialized in two ways:
//	   for a new file, by modifying the in-memory data structure
//	     to point to the newly allocated data blocks
//	   for a file already on disk, by reading the file header from disk
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"

#include "system.h"
#include "filehdr.h"
#include "indirectpointerblock.h"

//----------------------------------------------------------------------
// FileHeader::Allocate
// 	Initialize a fresh file header for a newly created file.
//	Allocate data blocks for the file out of the map of free disk blocks.
//	Return FALSE if there are not enough free blocks to accomodate
//	the new file.
//
//	"freeMap" is the bit map of free disk sectors
//	"fileSize" is the size
//----------------------------------------------------------------------


bool
FileHeader::Allocate(BitMap *freeMap, int fileSize)
{ 
	IndirectPointerBlock *ipb;

	// Mark all block pointers as empty
	clear();
	
    numBytes = fileSize;
    numSectors  = divRoundUp(fileSize, SectorSize);

	// TODO
	if(fileSize == 0)
		return true;

    if (freeMap->NumClear() < numSectors)
	{
		printf("Error: could not create file; disk is full\n");
		return false;		// not enough space
	}

	// Allocate temporary space for the ipbs we will be working with 
	ipb = new IndirectPointerBlock;

	int indirectBlock, freeSector;
    for (int i = 0; i < numSectors; i++)
    {
        if( i / 32 < NumIndirect)
        {
			indirectBlock = i / 32;

			// Check if it has already been assigned
			if(dataSectors[indirectBlock] == 0)
			{
				// If not the first IPB, write the previously made IPB to disk before we overwrite it
				if(indirectBlock != 0)
					ipb->WriteBack(dataSectors[indirectBlock-1]);
	
				//create a new indirect sector
				freeSector = freeMap->Find();
				if(freeSector == -1)
				{
					Deallocate(freeMap);
					delete ipb;
					return false;
				}
				dataSectors[i / 32] = freeSector;
				ipb->FetchFrom(dataSectors[i / 32]);
				ipb->clear();
			}

			// create a new direct sector and assign it to the indirect one
			freeSector = freeMap->Find();
			if(freeSector == -1)
			{
				Deallocate(freeMap);
				delete ipb;
				return false;
			}
			ipb->PutSector(freeSector);	
        }
		else
        {
            dataSectors[i] = freeMap->Find();
			if(freeSector == -1)
			{
				Deallocate(freeMap);
				delete ipb;
				return false;
			}
        }
    }
	ipb->WriteBack(dataSectors[indirectBlock]);
	delete ipb;
    return true;
}

//----------------------------------------------------------------------
// FileHeader::Deallocate
// 	De-allocate all the space allocated for data blocks for this file.
//
//	"freeMap" is the bit map of free disk sectors
//----------------------------------------------------------------------

void 
FileHeader::Deallocate(BitMap *freeMap)
{
	IndirectPointerBlock *ipb = new IndirectPointerBlock;
	
	// Recursively call deallocate on indirect blocks
	for(int i=0; i < NumIndirect; i++)
	{
		if(dataSectors[i] == 0)
			continue;
		ipb->FetchFrom(dataSectors[i]);
		ipb->Deallocate(freeMap);
		// Store the changes
		ipb->WriteBack(dataSectors[i]);
	}


    for (int i = 0; i < (NumDirect+NumIndirect); i++) 
	{
		if(dataSectors[i] == 0)
			continue;		
		ASSERT(freeMap->Test((int) dataSectors[i])); 
		freeMap->Clear((int) dataSectors[i]);
    }

	delete ipb;
}

//----------------------------------------------------------------------
// FileHeader::FetchFrom
// 	Fetch contents of file header from disk. 
//
//	"sector" is the disk sector containing the file header
//----------------------------------------------------------------------

void
FileHeader::FetchFrom(int sector)
{
    synchDisk->ReadSector(sector, (char *)this);
}

//----------------------------------------------------------------------
// FileHeader::WriteBack
// 	Write the modified contents of the file header back to disk. 
//
//	"sector" is the disk sector to contain the file header
//----------------------------------------------------------------------

void
FileHeader::WriteBack(int sector)
{
    synchDisk->WriteSector(sector, (char *)this); 
}

//----------------------------------------------------------------------
// FileHeader::ByteToSector
// 	Return which disk sector is storing a particular byte within the file.
//      This is essentially a translation from a virtual address (the
//	offset in the file) to a physical address (the sector where the
//	data at the offset is stored).
//
//	"offset" is the location within the file of the byte in question
//----------------------------------------------------------------------

int
FileHeader::ByteToSector(int offset)
{
	// This is necessary
	ASSERT(sizeof(IndirectPointerBlock) == SectorSize);

	// Check if the offset is held by a direct block
	if(offset >= TotalIndirectCapacity)
	{
		offset -= TotalIndirectCapacity;
		return dataSectors[(offset / SectorSize)+25];
	}

	// Offset held by an indirect block
	int indirectBlock = offset / IPCapacity;
	offset = offset % IPCapacity;

	// Read indirect sector off disk
	IndirectPointerBlock *ipb = new IndirectPointerBlock;
	ipb->FetchFrom(dataSectors[indirectBlock]);

	int sector = ipb->ByteToSector(offset);
	delete ipb;		
	return sector;
}

//----------------------------------------------------------------------
// FileHeader::FileLength
// 	Return the number of bytes in the file.
//----------------------------------------------------------------------

int
FileHeader::FileLength()
{
    return numBytes;
}

//----------------------------------------------------------------------
// FileHeader::Print
// 	Print the contents of the file header, and the contents of all
//	the data blocks pointed to by the file header.	
//----------------------------------------------------------------------

void
FileHeader::Print()
{
	return;
	// TODO
    int i, j, k;
    char *data = new char[SectorSize];

    printf("FileHeader contents.  File size: %d.  File blocks:\n", numBytes);
    for (i = 0; i < numSectors; i++)
	printf("%d ", dataSectors[i]);
    printf("\nFile contents:\n");
    for (i = k = 0; i < numSectors; i++) {
	synchDisk->ReadSector(dataSectors[i], data);
        for (j = 0; (j < SectorSize) && (k < numBytes); j++, k++) {
	    if ('\040' <= data[j] && data[j] <= '\176')   // isprint(data[j])
		printf("%c", data[j]);
            else
		printf("\\%x", (unsigned char)data[j]);
	}
        printf("\n"); 
    }
    delete [] data;
}

void FileHeader::clear()
{
	for(int i=0; i<NumPointers; i++)
	{
		dataSectors[i] = 0;
	}
}

bool FileHeader::extend(int newSize, BitMap *freeMap)
{
	// Allocate enough space to have 'newSize' sectors

	if(newSize > MaxFileSize)
		return false;

	int bytesLeftToAllocate = newSize - numBytes;
	int leftToAllocate = divRoundUp(newSize, SectorSize) - divRoundUp(numBytes, SectorSize);	

	// Check if we are trying to extend past the max size of the disk
	if(leftToAllocate > freeMap->NumClear())
		return false;

	int numFreePointers, freeSector;
	IndirectPointerBlock *ipb = new IndirectPointerBlock;
	// Attempt to store the needed blocks in our indirect blocks	
	for(int i=0; i<NumIndirect; i++)
	{
		// Allocate a new indirect block
		if(dataSectors[i] == 0)
		{
			freeSector = freeMap->Find();
			if(freeSector == -1)
			{
				delete ipb;
				return false;
			}
			dataSectors[i] = freeSector;
			ipb->FetchFrom(freeSector);
			ipb->clear();
		}

		// Check how many free pointer spots this indirect block has
		ipb->FetchFrom(dataSectors[i]);
		numFreePointers = ipb->getNumFreePointers();
		// Put as many sectors as we can / need into this indirect block
		for(int j=0; j<(leftToAllocate < numFreePointers ? leftToAllocate : numFreePointers); j++)
		{
			freeSector = freeMap->Find();
			if(freeSector == -1)
			{
				delete ipb;
				return false;
			}
			ipb->PutSector(freeSector);
			leftToAllocate--;			
			numBytes += SectorSize;	
		}

		ipb->WriteBack(dataSectors[i]);
		// Check if we are done
		if(leftToAllocate == 0)
			break;
	}


	// Need to use our remainign 5 direct blocks
	if(leftToAllocate > 0)
	{
		ASSERT(leftToAllocate <= 5);

		for(int i=NumIndirect; i < (NumIndirect + NumDirect); i++)
		{
			// Check if the direct pointer is unused
			if(dataSectors[i] == 0)
			{
				freeSector = freeMap->Find();
				if(freeSector == -1)
				{
					delete ipb;
					return false;
				}
				dataSectors[i] = freeSector;
				numBytes += SectorSize;	
				leftToAllocate--;
			}
			if(leftToAllocate == 0)
				break;
		}
		ASSERT(leftToAllocate == 0);
	}

	delete ipb;
	return true;
}




